import { useState, useEffect, useRef, useCallback } from "react";
import type { SelectProps, SelectValue } from "antd/lib/select";
import { Select } from "antd";
import { LoadingOutlined } from "@ant-design/icons";
import { get, omit } from "lodash";
import { isFunction } from "@/utils/is";
import { useTranslation } from "react-i18next";

interface OptionsItem {
	label: string;
	value: string | number;
	disabled?: boolean;
}

interface ApiSelectProps extends Omit<SelectProps<SelectValue>, "onChange"> {
	value?: SelectValue;
	numberToString?: boolean;
	api?: (arg?: any) => Promise<OptionsItem[]>;
	params?: any;
	resultField?: string;
	labelField?: string;
	valueField?: string;
	immediate?: boolean;
	alwaysLoad?: boolean;
	options?: OptionsItem[];
	ApiSelectRef?: any;
	ApiSelectPressEnter?: () => void;
	ApiSelectBlur?: () => void;
	onOptionsChange?: (options: OptionsItem[]) => void;
	onChange?: (value: SelectValue, option?: any) => void;
}

const ApiSelect = ({
	value,
	numberToString = false,
	api,
	params = {},
	resultField = "",
	labelField = "label",
	valueField = "value",
	immediate = true,
	alwaysLoad = false,
	options: initialOptions = [],
	ApiSelectRef,
	onOptionsChange,
	onChange,
	ApiSelectPressEnter,
	ApiSelectBlur,
	...restProps
}: ApiSelectProps) => {
	const [state, setState] = useState<SelectValue>(value);
	const [loading, setLoading] = useState(false);
	const isFirstLoaded = useRef(false);
	const [optionsItem, setOptionsItem] = useState([]);
	const { t } = useTranslation();

	useEffect(() => {
		if (immediate && !isFirstLoaded.current) {
			fetchOptions();
		}
	}, [params]);

	useEffect(() => {
		setState(value);
	}, [value]);

	const fetchOptions = useCallback(async () => {
		if (!api || !isFunction(api) || loading) return;
		try {
			setLoading(true);
			const res: any = await api(params);
			isFirstLoaded.current = true;
			let fetchedOptions: OptionsItem[] = [];
			if (Array.isArray(res)) {
				fetchedOptions = res;
			} else if (Array.isArray(res.list)) {
				fetchedOptions = res.list;
			} else if (resultField) {
				fetchedOptions = get(res, resultField) || [];
			}
			const processedOptions: any = fetchedOptions.map(item => ({
				...omit(item, [labelField, valueField]),
				label: get(item, labelField),
				value: numberToString ? `${get(item, valueField)}` : get(item, valueField)
			}));
			if (onOptionsChange) onOptionsChange(processedOptions);
			setOptionsItem(processedOptions);
			setState(value);
		} catch (error) {
			console.warn(error);
		} finally {
			setLoading(false);
		}
	}, [api, params, resultField, labelField, valueField, numberToString, onOptionsChange, value]);

	const handleDropdownVisibleChange = useCallback(
		async (open: boolean) => {
			if (open) {
				if (alwaysLoad || (!immediate && !isFirstLoaded.current)) {
					await fetchOptions();
				}
			}
		},
		[alwaysLoad, immediate]
	);

	return (
		<Select
			{...restProps}
			value={state}
			onChange={onChange}
			dropdownMatchSelectWidth={false}
			notFoundContent={
				loading ? (
					<span>
						<LoadingOutlined spin /> {t("component.form.apiSelectNotFound")}
					</span>
				) : undefined
			}
			suffixIcon={loading ? <LoadingOutlined spin /> : undefined}
			onDropdownVisibleChange={handleDropdownVisibleChange}
			options={initialOptions.length === 0 ? optionsItem : initialOptions}
			ref={ApiSelectRef}
			onPressEnter={ApiSelectPressEnter}
			onBlur={ApiSelectBlur}
		></Select>
	);
};

export default ApiSelect;
